<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <meta name="robots" content="noindex, nofollow">
    <style>
        body {
            padding: 0;
            margin: 0;
        }

        svg:not(:root) {
            display: block;
        }

        .playable-code {
            background-color: #f4f7f8;
            border: none;
            border-left: 6px solid #558abb;
            border-width: medium medium medium 6px;
            color: #4d4e53;
            height: 100px;
            width: 90%;
            padding: 10px 10px 0;
        }

        .playable-canvas {
            border: 1px solid #4d4e53;
            border-radius: 2px;
        }

        .playable-buttons {
            text-align: right;
            width: 90%;
            padding: 5px 10px 5px 26px;
        }
    </style>

    <style>
        body {
            font-family: "Open Sans", "Lucida Grande", "Arial", sans-serif;
            font-size: 16px;
        }

        .logBox {
            margin-top: 16px;
            width: 400px;
            height:500px;
            border-radius: 6px;
            border: 1px solid black;
            box-shadow: 4px 4px 2px black;
        }

        .logHeader {
            margin: 0;
            padding: 0 6px 4px;
            height: 22px;
            background-color: lightblue;
            border-bottom: 1px solid black;
            border-radius: 6px 6px 0 0;
        }

        #log {
            font: 12px "Courier", monospace;
            padding: 6px;
            overflow: auto;
            overflow-y: scroll;
            width: 388px;
            height: 460px;
        }

        .container {
            width: 400px;
            padding: 6px;
            border-radius: 6px;
            border: 1px solid black;
            box-shadow: 4px 4px 2px black;
            display: block;
            overflow: auto;
        }

        .label {
            display: inline-block;
        }

        .counter {
            text-align: right;
            padding-top: 4px;
            float: right;
        }

        .button {
            padding-top: 2px;
            padding-bottom: 4px;
            width: 100px;
            display: inline-block;
            float: left;
            border: 1px solid black;
            cursor: pointer;
            text-align: center;
            margin-top: 0;
            color: white;
            background-color: darkgreen;
        }

        #progress {
            width: 100%;
            padding-top: 6px;
        }

    </style>

    <title>Background Tasks API - 示例 - code sample</title>

</head>
<body>

<p>
    演示使用 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API">
    协作调度幕后任务 </a> 使用 <code>requestIdleCallback()</code>
    方法。
</p>

<div class="container">
    <div class="label">解码量子丝极谱发射中...</div>
    <progress id="progress" value="0"></progress>
    <div class="button" id="startButton">
        开始
    </div>
    <div class="label counter">
        任务 <span id="currentTaskNumber">0</span> / <span id="totalTaskCount">0</span>
    </div>
</div>

<div class="logBox">
    <div class="logHeader">
        记录
    </div>
    <div id="log">
    </div>
</div>

<script>
    let taskList = [];
    let totalTaskCount = 0;
    let currentTaskNumber = 0;
    let taskHandle = null;

    let totalTaskCountElem = document.getElementById("totalTaskCount");
    let currentTaskNumberElem = document.getElementById("currentTaskNumber");
    let progressBarElem = document.getElementById("progress");
    let startButtonElem = document.getElementById("startButton");
    let logElem = document.getElementById("log");

    let logFragment = null;
    let statusRefreshScheduled = false;

    window.requestIdleCallback = window.requestIdleCallback || function(handler) {
        let startTime = Date.now();

        return setTimeout(function() {
            handler({
                didTimeout: false,
                timeRemaining: function() {
                    return Math.max(0, 50.0 - (Date.now() - startTime));
                }
            });
        }, 1);
    };

    window.cancelIdleCallback = window.cancelIdleCallback || function(id) {
        clearTimeout(id);
    };

    function enqueueTask(taskHandler, taskData) {
        taskList.push({
            handler: taskHandler,
            data: taskData
        });

        totalTaskCount++;

        if (!taskHandle) {
            taskHandle = requestIdleCallback(runTaskQueue, { timeout: 1000 });
        }

        scheduleStatusRefresh();
    }

    function runTaskQueue(deadline) {
        while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && taskList.length) {
            let task = taskList.shift();
            currentTaskNumber++;

            task.handler(task.data);
            scheduleStatusRefresh();
        }

        if (taskList.length) {
            taskHandle = requestIdleCallback(runTaskQueue, { timeout: 1000} );
        } else {
            taskHandle = 0;
        }
    }

    function scheduleStatusRefresh() {
        if (!statusRefreshScheduled) {
            requestAnimationFrame(updateDisplay);
            statusRefreshScheduled = true;
        }
    }

    function updateDisplay() {
        let scrolledToEnd = logElem.scrollHeight - logElem.clientHeight <= logElem.scrollTop + 1;

        if (totalTaskCount) {
            if (progressBarElem.max != totalTaskCount) {
                totalTaskCountElem.textContent = totalTaskCount;
                progressBarElem.max = totalTaskCount;
            }

            if (progressBarElem.value != currentTaskNumber) {
                currentTaskNumberElem.textContent = currentTaskNumber;
                progressBarElem.value = currentTaskNumber;
            }
        }

        if (logFragment) {
            logElem.appendChild(logFragment);
            logFragment = null;
        }

        if (scrolledToEnd) {
            logElem.scrollTop = logElem.scrollHeight - logElem.clientHeight;
        }

        statusRefreshScheduled = false;
    }

    function log(text) {
        if (!logFragment) {
            logFragment = document.createDocumentFragment();
        }

        let el = document.createElement("div");
        el.innerHTML = text;
        logFragment.appendChild(el);
    }

    function logTaskHandler(data) {
        log("<strong>Running task #" + currentTaskNumber + "</strong>");

        for (i=0; i<data.count; i+=1) {
            log((i+1).toString() + ". " + data.text);
        }
    }

    function getRandomIntInclusive(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    function decodeTechnoStuff() {
        totalTaskCount = 0;
        currentTaskNumber = 0;
        updateDisplay();

        let n = getRandomIntInclusive(100, 500);

        for (i=0; i<n; i++) {
            let taskData = {
                count: getRandomIntInclusive(75, 150),
                text: "This text is from task number " + (i+1).toString() + " of " + n
            };

            enqueueTask(logTaskHandler, taskData);
        }
    }

    startButtonElem.addEventListener("click", decodeTechnoStuff, false);

</script>

</body>
</html>
